/*
 * $Id: MatteBorderExt.java 4147 2012-02-01 17:13:24Z kschaefe $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package org.jdesktop.swingx.border;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Insets;

import javax.swing.Icon;
import javax.swing.border.MatteBorder;

import org.jdesktop.beans.JavaBean;

/**
 * Matte border that allows specialized icons for corners and sides.
 *
 * @author Ramesh Gupta
 */
@JavaBean
public class MatteBorderExt extends MatteBorder {
	protected Icon[] tileIcons = null;
	private Icon defaultIcon = null;

	/**
	 * Draws a matte border using specialized icons for corners and sides. If
	 * tileIcons is null, or if the length of tileIcons array is less than 2,
	 * this defaults to the {@link javax.swing.border.MatteBorder superclass}
	 * behavior. Otherwise, tileIcons must specify icons in clockwise order,
	 * starting with the top-left icon at index zero, culminating with the left
	 * icon at index 7. If the length of the tileIcons array is greater than 1,
	 * but less than 8, then tileIcons[0] is used to paint the corners, and
	 * tileIcons[1] is used to paint the sides, with icons rotated as necessary.
	 * Other icons, if any, are ignored.
	 *
	 * @param top
	 *            top inset
	 * @param left
	 *            left inset
	 * @param bottom
	 *            bottom inset
	 * @param right
	 *            right inset
	 * @param tileIcons
	 *            array of icons starting with top-left in index 0, continuing
	 *            clockwise through the rest of the indices
	 */
	public MatteBorderExt(int top, int left, int bottom, int right, Icon[] tileIcons) {
		super(top, left, bottom, right, (tileIcons == null) || (tileIcons.length == 0) ? null : tileIcons[0]);
		this.tileIcons = tileIcons;
	}

	/**
	 * @see MatteBorder#MatteBorder(int, int, int, int, java.awt.Color)
	 */
	public MatteBorderExt(int top, int left, int bottom, int right, Color matteColor) {
		super(top, left, bottom, right, matteColor);
	}

	/**
	 * @see MatteBorder#MatteBorder(java.awt.Insets, java.awt.Color)
	 */
	public MatteBorderExt(Insets borderInsets, Color matteColor) {
		super(borderInsets, matteColor);
	}

	/**
	 * @see MatteBorder#MatteBorder(int, int, int, int, javax.swing.Icon)
	 */
	public MatteBorderExt(int top, int left, int bottom, int right, Icon tileIcon) {
		super(top, left, bottom, right, tileIcon);
	}

	/**
	 * @see MatteBorder#MatteBorder(java.awt.Insets, javax.swing.Icon)
	 */
	public MatteBorderExt(Insets borderInsets, Icon tileIcon) {
		super(borderInsets, tileIcon);
	}

	/**
	 * @see MatteBorder#MatteBorder(javax.swing.Icon)
	 */
	public MatteBorderExt(Icon tileIcon) {
		super(tileIcon);
	}

	/**
	 * Returns the icons used by this border
	 *
	 * @return the icons used by this border
	 */
	public Icon[] getTileIcons() {
		return tileIcons;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
		if ((tileIcons == null) || (tileIcons.length < 2)) {
			super.paintBorder(c, g, x, y, width, height);
			return;
		}

		Insets insets = getBorderInsets(c);
		int clipWidth, clipHeight;

		clipWidth = Math.min(width, insets.left); // clip to component width or
													// insets
		clipHeight = Math.min(height, insets.top); // clip to component height
													// or insets

		if ((clipWidth <= 0) || (clipHeight <= 0)) {
			return; // nothing to paint
		}

		// We now know that we have at least two icons!

		Color oldColor = g.getColor(); // restore before exiting
		g.translate(x, y); // restore before exiting

		for (int i = 0; i < tileIcons.length; i++) {
			// Make sure we have an icon to paint with
			if (tileIcons[i] == null) {
				tileIcons[i] = getDefaultIcon();
			}
		}

		paintTopLeft(c, g, 0, 0, insets.left, insets.top);
		paintTop(c, g, insets.left, 0, width - insets.left - insets.right, insets.top);
		paintTopRight(c, g, width - insets.right, 0, insets.right, insets.top);
		paintRight(c, g, width - insets.right, insets.top, insets.right, height - insets.top - insets.bottom);
		paintBottomRight(c, g, width - insets.right, height - insets.bottom, insets.right, insets.bottom);
		paintBottom(c, g, insets.left, height - insets.bottom, width - insets.left - insets.right, insets.bottom);
		paintBottomLeft(c, g, 0, height - insets.bottom, insets.left, insets.bottom);
		paintLeft(c, g, 0, insets.top, insets.left, height - insets.top - insets.bottom);

		g.translate(-x, -y); // restore
		g.setColor(oldColor); // restore

	}

	protected void paint(Icon icon, Component c, Graphics g, int x, int y, int width, int height) {
		Graphics cg = g.create();

		try {
			cg.setClip(x, y, width, height);
			int tileW = icon.getIconWidth();
			int tileH = icon.getIconHeight();
			int xpos, ypos, startx, starty;
			for (ypos = 0; height - ypos > 0; ypos += tileH) {
				for (xpos = 0; width - xpos > 0; xpos += tileW) {
					icon.paintIcon(c, cg, x + xpos, y + ypos);
				}
			}
		} finally {
			cg.dispose();
		}
	}

	/**
	 * Only called by paintBorder()
	 */
	protected void paintTopLeft(Component c, Graphics g, int x, int y, int width, int height) {
		Graphics cg = g.create();

		try {
			cg.setClip(x, y, width, height);
			tileIcons[0].paintIcon(c, cg, x, y);
		} finally {
			cg.dispose();
		}
	}

	/**
	 * Only called by paintBorder()
	 */
	protected void paintTop(Component c, Graphics g, int x, int y, int width, int height) {
		paint(tileIcons[1], c, g, x, y, width, height);
	}

	/**
	 * Only called by paintBorder()
	 */
	protected void paintTopRight(Component c, Graphics g, int x, int y, int width, int height) {
		if (tileIcons.length == 8) {
			paint(tileIcons[2], c, g, x, y, width, height);
		} else {
			Icon icon = tileIcons[0];
			/** @todo Rotate -90 and paint icon */
		}
	}

	/**
	 * Only called by paintBorder()
	 */
	protected void paintRight(Component c, Graphics g, int x, int y, int width, int height) {
		if (tileIcons.length == 8) {
			paint(tileIcons[3], c, g, x, y, width, height);
		} else {
			Icon icon = tileIcons[1];
			/** @todo Rotate -90 and paint icon */
		}
	}

	/**
	 * Only called by paintBorder()
	 */
	protected void paintBottomRight(Component c, Graphics g, int x, int y, int width, int height) {
		if (tileIcons.length == 8) {
			paint(tileIcons[4], c, g, x, y, width, height);
		} else {
			Icon icon = tileIcons[0];
			/** @todo Rotate -180 and paint icon */
		}
	}

	/**
	 * Only called by paintBorder()
	 */
	protected void paintBottom(Component c, Graphics g, int x, int y, int width, int height) {
		if (tileIcons.length == 8) {
			paint(tileIcons[5], c, g, x, y, width, height);
		} else {
			Icon icon = tileIcons[1];
			/** @todo Rotate -180 and paint icon */
		}
	}

	/**
	 * Only called by paintBorder()
	 */
	protected void paintBottomLeft(Component c, Graphics g, int x, int y, int width, int height) {
		if (tileIcons.length == 8) {
			paint(tileIcons[6], c, g, x, y, width, height);
		} else {
			Icon icon = tileIcons[0];
			/** @todo Rotate -270 and paint icon */
		}
	}

	/**
	 * Only called by paintBorder()
	 */
	protected void paintLeft(Component c, Graphics g, int x, int y, int width, int height) {
		if (tileIcons.length == 8) {
			paint(tileIcons[7], c, g, x, y, width, height);
		} else {
			Icon icon = tileIcons[1];
			/** @todo Rotate -270 and paint icon */
		}
	}

	/**
	 * Only called by paintBorder()
	 */
	protected Icon getDefaultIcon() {
		if (defaultIcon == null) {
			defaultIcon = new Icon() {
				private int width = 3;
				private int height = 3;

				public int getIconWidth() {
					return width;
				}

				public int getIconHeight() {
					return height;
				}

				public void paintIcon(Component c, Graphics g, int x, int y) {
					g.setColor(c.getBackground().darker().darker());
					// g.translate(x, y);
					g.fillRect(x, y, width, height);
				}
			};
		}
		return defaultIcon;
	}
}